﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using TicTacToe._3D;

namespace TicTacToe.UI.Components
{
    partial class GameView2D : UserControl
    {
        readonly Image[] mImages;
        readonly DrawingGroup[] mPreviews;
        readonly DrawingGroup[] mFields;
        readonly HashSet<Triple> mTurns;

        Triple mLastPreviewPoint = new Triple(-1, -1, -1);
        int mMouseMoveCounter;
        bool mIsLocked;

        public event EventHandler<PointEventArgs> OnPreviewUpdate;
        public event EventHandler<PointEventArgs> OnNewTurn;
        public event EventHandler<EventArgs> OnClearPreview;

        public GameView2D()
        {
            InitializeComponent();

            mImages = new[] { image1, image2, image3, image4 };
            mPreviews = new[] { preview1, preview2, preview3, preview4 };
            mFields = new[] { field1, field2, field3, field4 };
            mTurns = new HashSet<Triple>();
        }

        #region Public Methods

        public void ClearPreview()
        {
            foreach (var preview in mPreviews)
                preview.Children.Clear();
        }

        public void UpdatePreview(Triple point, Color color, bool isPlayerOne)
        {
            ClearPreview();

            var drawing = CreateDrawing(point.Item1, point.Item2, color, isPlayerOne, true);
            mPreviews[point.Item3].Children.Add(drawing);
        }

        public void UpdateGameView(Triple point, Color color, bool isPlayerOne)
        {
            var drawing = CreateDrawing(point.Item1, point.Item2, color, isPlayerOne, false);
            mFields[point.Item3].Children.Add(drawing);
            mTurns.Add(point);
        }

        public void SetLock(bool value)
        {
            mIsLocked = value;
        }

        #endregion

        #region WPF Drawing Helpers

        static Drawing CreateDrawing(int x, int y, Color color, bool isPlayerOne, bool isPreview)
        {
            return (isPlayerOne) ? CreateRectangle(x, y, color, isPreview) : CreateCircle(x, y, color, isPreview);
        }

        static GeometryDrawing CreateCircle(int x, int y, Color color, bool isPreview)
        {
            return new GeometryDrawing
            {
                Brush = new SolidColorBrush(color) { Opacity = (isPreview) ? 0.65 : 1 },
                Geometry = new RectangleGeometry(new Rect(100 * x + 10, 100 * y + 10, 80, 80), 40, 40),
            };
        }

        static GeometryDrawing CreateRectangle(int x, int y, Color color, bool isPreview)
        {
            return new GeometryDrawing
            {
                Brush = new SolidColorBrush(color) { Opacity = (isPreview) ? 0.65 : 1 },
                Geometry = new RectangleGeometry(new Rect(100 * x + 10, 100 * y + 10, 80, 80)),
            };
        }

        Triple GetPosition(Func<IInputElement, Point> getPosition)
        {
            Image i = null;
            int z;

            for (z = 0; z < mImages.Length; z++)
                if (mImages[z].IsMouseOver)
                {
                    i = mImages[z];
                    break;
                }

            var p = getPosition(i);
            var x = Math.Floor(p.X / (i.ActualWidth / 4));
            var y = Math.Floor(p.Y / (i.ActualHeight / 4));

            return new Triple((int)x, (int)y, z);
        }

        #endregion

        #region Handlers

        private void MouseLeft(object sender, MouseEventArgs e)
        {
            mMouseMoveCounter = 0;

            if (OnClearPreview != null)
                OnClearPreview(this, null);
        }

        private void MouseMoved(object sender, MouseEventArgs e)
        {
            if (mIsLocked)
                return;

            ++mMouseMoveCounter;
            if (mMouseMoveCounter % 6 != 0)
                return;
            else
                mMouseMoveCounter = 0;
                
            var point = GetPosition(e.GetPosition);
            if (mLastPreviewPoint != point && OnPreviewUpdate != null)
            {
                mLastPreviewPoint = point;
                OnPreviewUpdate(this, new PointEventArgs(point));
            }
        }

        private void MouseClicked(object sender, MouseButtonEventArgs e)
        {
            if (mIsLocked)
                return;

            var point = GetPosition(e.GetPosition);
            if (!mTurns.Contains(point) && OnNewTurn != null)
                OnNewTurn(this, new PointEventArgs(point));
        }

        #endregion
    }
}
